﻿using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using KC.Framework.Extension;
using KC.Domain.Core.EFRepository;
using KC.Model.Account;

namespace KC.DataAccess.Account.Repository
{
    public class OrganizationRepository : EFTreeRepositoryBase<Organization>, IOrganizationRepository
    {
        public OrganizationRepository(EFUnitOfWorkContextBase unitOfWork)
            : base(unitOfWork)
        {
        }

        //public OrganizationRepository(Tenant tenant)
        //    : base(new ComAccountUnitOfWorkContext(tenant))
        //{
        //}

        public IQueryable<Organization> GetRootOrganizations(Expression<Func<Organization, bool>> predicate)
        {
            return EFContext
                .Set<Organization>()
                .Where(predicate).OrderBy(m => m.Index)
                .AsNoTracking();
        }

        public IList<Organization> GetOrganizationList(Expression<Func<Organization, bool>> predicate)
        {
            //https://stackoverflow.com/questions/17195047/entity-framework-self-referencing-entity-query-results-are-flat-and-hiearchical/17221404#17221404
            var subset = new List<Organization>();
            var query = Entities.Where(predicate);
            foreach (var disp in query.OrderBy(x => x.ParentId).ToArray())
            {
                if (!disp.ParentId.HasValue)
                    subset.Add(disp);
                else
                    break;
            }

            return subset;
        }

        public IList<Organization> GetOrganizationsWithUsersByFilter<K>(Expression<Func<Organization, bool>> predicate,
            Expression<Func<Organization, K>> keySelector, bool ascending = true)
        {
            if (@ascending)
            {
                var orgs = Entities
                    .Include(m => m.OrganizationUsers)
                    .ThenInclude(r => r.User)
                    .Where(predicate)
                    .AsNoTracking()
                    .OrderBy(keySelector)
                    .ToList();
                orgs.ForEach(k =>
                {
                    k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => true).ToList();
                });

                return orgs;
            }

            var descOrgs = Entities
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .Where(predicate)
                .AsNoTracking()
                .OrderBy(keySelector)
                .ToList();
            descOrgs.ForEach(k =>
            {
                k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => true).ToList();
            });
            return  descOrgs;
        }

        public IList<Organization> GetAllOrganizationsWithUsers(Expression<Func<Organization, bool>> predicate)
        {
            Expression<Func<User, bool>> sqlWhere = c => true;
  
            var orgs = EFContext.Set<Organization>()
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .Where(predicate)
                .AsNoTracking()
                .ToList();
            orgs.ForEach(k =>
            {
                k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => true).ToList();
            });
            return orgs;
        }

        public IList<Organization> GetAllOrganizationsWithUsersByUser(Expression<Func<Organization, bool>> predicate, string userName, string userCode, string searchKey)
        {
            Expression<Func<User, bool>> sqlWhere = c => true;
            var orgs = EFContext.Set<Organization>()
            .Include(m => m.OrganizationUsers)
            .ThenInclude(r => r.User)
            .Where(predicate)
            .AsNoTracking()
            .ToList();
            orgs.ForEach(k =>
            {
                if (!string.IsNullOrWhiteSpace(userName) || !string.IsNullOrWhiteSpace(userCode))
                {
                    if (searchKey=="usercode"||searchKey=="username")
                    {
                        switch (searchKey)
                        {
                            case "username":
                                k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => m.DisplayName.Contains(userName)).ToList();
                                break;
                            case "usercode":
                                k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => m.ReferenceId1.Contains(userCode)).ToList();
                                break;
                        }
                        orgs = orgs.Where(c => c.OrganizationUsers.Count > 0).ToList();
                    }
                    else //加过滤条件为部门的时,选择弹框人员只需未删除的
                    {
                        k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => true).ToList();
                    }
                    
                }
                else
                {
                    k.OrganizationUsers = GetValidUsers(k.OrganizationUsers, m => true).ToList();

                }
            });
            return orgs;
        }

        public Organization GetOrganizationsAndUsersByOrgId(int orgId)
        {
            var orgs = EFContext.Set<Organization>()
                .Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.ChildNodes)))
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .AsNoTracking()
                 .FirstOrDefault(m => m.Id.Equals(orgId));

            orgs.OrganizationUsers = GetValidUsers(orgs.OrganizationUsers, m => true).ToList();

            return orgs;
        }

        public Organization GetUsersByOrganizationId(int id)
        {
            var orgs = EFContext.Set<Organization>()
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .AsNoTracking()
                .FirstOrDefault(m => m.Id.Equals(id));
            orgs.OrganizationUsers = GetValidUsers(orgs.OrganizationUsers, m => true).ToList();

            return orgs;
        }

        /// <summary>
        /// 只能获取所管理下三级组织的用户列表
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public List<User> GetUsersByOrgId(int id)
        {
            var result = new List<User>();
            var org = EFContext.Set<Organization>()
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .Include(m => m.ChildNodes.Select(c => c.OrganizationUsers))
                .Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.OrganizationUsers)))
                .Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.ChildNodes.Select(e => e.OrganizationUsers))))
                .AsNoTracking()
                .FirstOrDefault(m => m.Id.Equals(id));

            GetNestedUsers(org, ref result);

            return result;
        }

        /// <summary>
        /// 根据部门id获取部门下人员,不含下级部门
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public List<User> GetUsersByOrgIdWithoutOrg(int id)
        {
            var result = new List<User>();
            var org = EFContext.Set<Organization>()
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                //.Include(m => m.ChildNodes.Select(c => c.AspNetUsers))
                //.Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.AspNetUsers)))
                //.Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.ChildNodes.Select(e => e.AspNetUsers))))
                .AsNoTracking()
                .FirstOrDefault(m => m.Id.Equals(id));

            GetNestedUsers(org, ref result);

            return result;


        }

        /// <summary>
        /// 只能获取所管理下三级组织的用户列表
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public List<User> GetUsersByOrgIds(List<int> ids)
        {
            var result = new List<User>();
            var orgs = EFContext.Set<Organization>()
                .Include(m => m.OrganizationUsers)
                .ThenInclude(r => r.User)
                .Include(m => m.ChildNodes.Select(c => c.OrganizationUsers))
                .Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.OrganizationUsers)))
                .Include(m => m.ChildNodes.Select(c => c.ChildNodes.Select(d => d.ChildNodes.Select(e => e.OrganizationUsers))))
                .AsNoTracking()
                .Where(m => ids.Contains(m.Id))
                .ToList();

            orgs.ForEach(m => GetNestedUsers(m, ref result));

            return result;
        }

        /// <summary>
        /// 根据父级部门Id集合获取所有子部门
        /// </summary>
        /// <param name="parentOrganizationIds">父亲级部门Ids</param>
        /// <returns></returns>
        public List<Organization> GetAllOrganizationsByParentOrganizationIds(List<int?> parentOrganizationIds)
        {
            List<Organization> organizationList = new List<Organization>();
            foreach (var parentOrganizationId in parentOrganizationIds)
            {
                var organizations = GetAllOrganizationsByParentOrganizationId(parentOrganizationId);
                organizationList.AddRange(organizations);
            }
            return organizationList;
        }

        private List<Organization> GetAllOrganizationsByParentOrganizationId(int? parentOrganizationId)
        {
            var orgnizations =
                Entities.Where(o => !o.IsDeleted && o.ParentId == parentOrganizationId).ToList();

            List<Organization> childOrganizations = new List<Organization>();
            foreach (var orgnization in orgnizations)
            {
                childOrganizations.AddRange(GetAllOrganizationsByParentOrganizationId(orgnization.Id));
            }
            orgnizations.AddRange(childOrganizations);
            return orgnizations;
        }

        private void GetNestedUsers(Organization parentOrg, ref List<User> result)
        {
            if (parentOrg == null)
                return;

            result.AddRange(parentOrg.OrganizationUsers.Select(u => u.User));
            foreach (var childOrg in parentOrg.ChildNodes)
            {
                GetNestedUsers(childOrg, ref result);
            }
        }

        private IEnumerable<UsersInOrganizations> GetValidUsers(IEnumerable<UsersInOrganizations> usersInOrgs, Func<User, bool> predicate)
        {
            var users = usersInOrgs.Select(u => u.User).Where(predicate).Select(m => m.Id);
            return usersInOrgs.Where(m => users.Contains(m.UserId));
        }

    }
}
